#ifndef _SWF_UI_H_
#define _SWF_UI_H_

#include <string>
#include <vector>

#ifdef SWFUI_BUILD_DLL
#define SWFUI_API __declspec(dllexport)
#else
#define SWFUI_API __declspec(dllimport)
#endif

#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4251)
#endif

namespace ShockwaveFlashObjects
{
	struct IShockwaveFlash;
}

struct IOleInPlaceObjectWindowless;

namespace SwfUI
{
	namespace detail
	{
		class FlashContainer;
		class EventHandler;
	}

	/// Different types of arguments in a function call
	enum ArgumentType
	{	/// The argument does not have a value
		AT_NULL,
		/// The argument is a number
		AT_NUMERIC,
		/// The argument is a boolean
		AT_BOOLEAN,
		/// The argument is a string
		AT_STRING
	};

	/// This class provides an interface to pass arguments between ActionScript and C++
	class SWFUI_API Argument
	{
	public:
		/// Create an argument of type AT_NULL
		Argument();
		/// Copy constructor
		Argument(const Argument& other);
		/// Create an argument of type AT_NUMERIC
		Argument(int value);
		/// Create an argument of type AT_NUMERIC
		Argument(float value);
		/// Create an argument of type AT_BOOLEAN
		Argument(bool value);
		/// Create an argument of type AT_STRING
		Argument(const std::wstring& value);

		/// Custom constructor
		template<typename T>
		Argument(const T& value)
		{
			char unknown_data_type_[0];//you have to provide your own implementation
		}

		/// Get argument value as a float
		operator float() const;
		/// Get argument value as a bool
		operator bool() const;
		/// Get argument value as a string
		operator const std::wstring& () const;
		/// Custom getter
		template<typename T>
		operator T () const
		{
			char unknown_data_type_[0];//you have to provide your own implementation
		}
		/// Get the type of argument
		ArgumentType getType() const {return type;}
		/// An argument of type AT_NULL
		static Argument Null;

	private:
		mutable union
		{
			float numeric;
			bool boolean;
		}value;
		mutable std::wstring stringValue;
		ArgumentType type;
	};

	///This class represent a list of argument
	class SWFUI_API ArgumentList: public std::vector<Argument>
	{
	public:
		ArgumentList();
		///Construct an argument list
		/*!
			\param firstArgument First argument in the list
		*/
		ArgumentList(const Argument& firstArgument);
		///Add an argument to the list
		/*!
			\param arg The argument to add
			\remarks 
			Calls can be chained.
			For example:
\code
ArgumentList args;
args(firstArg)(secondArg)(thirdArg);
\endcode
		*/
		ArgumentList& operator()(const Argument& arg);
	};

	/// Types of mouse event to be injected
	enum MouseEventType
	{
		MET_MOUSELUP,
		MET_MOUSELDOWN,
		MET_MOUSERUP,
		MET_MOUSERDOWN,
		MET_MOUSEMUP,
		MET_MOUSEMDOWN,
		MET_MOUSEMOVED,
		MET_MOUSEWHEEL
	};

	/// Render quality
	enum RenderQuality
	{
		RQ_LOW,
		RQ_MEDIUM,
		RQ_HIGH,
		RQ_BEST,
		RQ_AUTOLOW,
		RQ_AUTOHIGH
	};

	/// This class is the actual representation of a flash control
	class SWFUI_API Control
	{
		friend class detail::FlashContainer;
		friend class detail::EventHandler;
	public:
		/// Create a control
		/*!
			\param width Width of the control
			\param height Height of the control
		*/
		Control(int width, int height);
		virtual ~Control();

		/// Load an swf file
		/*!
			\param swfFile path to file
		*/
		void load(const std::wstring& swfFile);
		/// Inject a mouse event
		void injectMouse(MouseEventType type, int x, int y, int wheel);
		/// Inject keyboard input
		/// \internal
		void injectKey(void* msg);
		/// Change render quality
		void setRenderQuality(RenderQuality quality);
		/// Check whether a point is in the control
		bool isPointInControl(int x, int y) const;
		/// Change transparency
		void setTransparent(bool transparent);
		/// Call a flash function
		/*!
			\param functionName Name of the function
			\param args List of arguments
			\return Result
		*/
		Argument callFunction(const std::wstring& functionName, const ArgumentList& args);

		/// Called when flash calls a C++ function through ExternalInterface
		/*!
			\param functionName Name of the function
			\param args List of arguments
			\return Result
		*/
		virtual Argument onFlashCall(const std::wstring& functionName, const ArgumentList& args);
		/// Called when flash calls fsCommand
		/*!
			\param command Name of the command
			\param args Arguments
		*/
		virtual void onFSCommand(const wchar_t* command, const wchar_t* args);
		/// Called when a region is invalidated
		virtual void invalidateRect(int left, int top, int right, int bottom) = 0;
		/// Transform a point from global coordinate to local coordinate
		virtual void transformToLocal(int& x, int& y) const = 0;
		
	protected:
		/// The OLE container
		detail::FlashContainer* container;
		/// The event handler
		detail::EventHandler* eventHandler;
		/// The COM interface to the embeded flash object
		ShockwaveFlashObjects::IShockwaveFlash* flashObject;
		/// An inplace object to inject input to flash
		IOleInPlaceObjectWindowless* windowlessObj;
		/// Width of the control
		int width;
		/// Height of the control
		int height;
		/// Whether the control is transparent
		bool transparent;
	};

	/// Initialize the system. Must be called before using any SwfUI functions
	SWFUI_API void initialize();
	/// Deinitialize the system. Must be called at the end of the program
	SWFUI_API void shutdown();
	/// Allow a control to receive keyboard input
	/// \param control The control to receive input. If control is NULL. No controls will receive input.
	SWFUI_API void setFocus(Control* control);
	/// Get the currently focused control
	SWFUI_API Control* getFocus();
}

#ifdef _MSC_VER
#pragma warning(pop)
#endif

#endif